﻿using System;

namespace MiniGert
{
  /// <summary>
  /// Rectangle is an axially aligned bounding box. It is defined by the boundaries
  /// left, top, right and bottom which are combined into the two points LeftTop and RightBottom.
  /// </summary>
  public class Rectangle
  {
    private Point _leftTop = null;
    private Point _rightBottom = null;
    private Point _center = new Point();

    /// <summary>
    /// Creates a new rectangle at centered at (0, 0) with width 0 and height 0.
    /// </summary>
    public Rectangle()
    {
      _leftTop = new Point();
      _rightBottom = new Point();
    }

    /// <summary>
    /// Creates a new rectangle defined by the given points.
    /// </summary>
    /// <param name="leftTop">A point representing the left and top boundaries.</param>
    /// <param name="rightBottom">A point representing the right and bottom boundaries.</param>
    public Rectangle(Point leftTop, Point rightBottom)
    {
      _leftTop = leftTop;
      _rightBottom = rightBottom;
    }

    /// <summary>
    /// Creates a new rectangle defined by the given values.
    /// </summary>
    /// <param name="left">The left boundary of the rectangle</param>
    /// <param name="top"></param>
    /// <param name="right"></param>
    /// <param name="bottom"></param>
    public Rectangle(float left, float top, float right, float bottom)
    {
      _leftTop = new Point(left, top);
      _rightBottom = new Point(right, bottom);
    }

    /// <summary>
    /// Gets or sets the left and top boundaries of the rectangle.
    /// The X value of the point corresponds to left, and Y to right.
    /// </summary>
    public Point LeftTop
    {
      get { return _leftTop; }
      set { _leftTop = value; }
    }

    /// <summary>
    /// Gets or sets the right and bottom boundaries of the rectangle.
    /// The X value of the point corresponds to right, and Y to bottom.
    /// </summary>
    public Point RightBottom
    {
      get { return _rightBottom; }
      set { _rightBottom = value; }
    }

    /// <summary>
    /// Gets the width of the rectangle by calculating <code>right - left</code>.
    /// </summary>
    public float Width
    {
      get { return _rightBottom.X - _leftTop.X; }
    }

    /// <summary>
    /// Gets the height of the rectangle by calculating <code>bottom - top</code>.
    /// </summary>
    public float Height
    {
      get { return _rightBottom.Y - _leftTop.Y; }
    }

    /// <summary>
    /// Calculates the center of rectangle.
    /// </summary>
    /// <returns>The center of the rectangle.</returns>
    public Point GetCenter()
    {
      _center.X = _leftTop.X + ((_rightBottom.X - _leftTop.X) / 2.0f);
      return _center;
    }

    /// <summary>
    /// Calculates the point of the rectangle closest to the given point.
    /// If the point is inside of the rectangle than the given point is returned.
    /// </summary>
    /// <param name="point">The point.</param>
    /// <returns>The point of the rectangle closest to the given point.</returns>
    public Point ClosestPoint(Point point)
    {
      return ClosestPoint(point.X, point.Y);
    }

    /// <summary>
    /// Calculates the given point of the rectangle closest to the given point.
    /// If the point is inside of the rectangle than the given point is returned.
    /// </summary>
    /// <param name="x">The x component of the point.</param>
    /// <param name="y">The y component of the point.</param>
    /// <returns>The point of the rectangle closest to the given point.</returns>
    public Point ClosestPoint(float x, float y)
    {
      Point result = new Point();

      if (x < _leftTop.X)
        result.X = _leftTop.X;
      else if (x > _rightBottom.X)
        result.X = _rightBottom.X;
      else
        result.X = x;

      if (y < _leftTop.Y)
        result.Y = _leftTop.Y;
      else if (y > _rightBottom.Y)
        result.Y = _rightBottom.Y;
      else
        result.Y = y;

      return result;
    }

    /// <summary>
    /// Checks if the point lies within the rectangle. This includes the outer
    /// boundaries of the rectangle.
    /// </summary>
    /// <param name="point">The point to be checked.</param>
    /// <returns>True if the point is inside of the rectangle, else false.</returns>
    public bool PointIsInside(Point point)
    {
      return (point.X >= _leftTop.X) && (point.X <= _rightBottom.X) &&
             (point.Y >= _leftTop.Y) && (point.Y <= _rightBottom.Y);
    }
    /// <summary>
    /// Checks if the given point lies within the rectangle. This includes the outer
    /// boundaries of the rectangle.
    /// </summary>
    /// <param name="x">The x component of the point.</param>
    /// <param name="y">The x component of the point.</param>
    /// <returns>True if the point is inside of the rectangle, else false.</returns>
    public bool PointIsInside(float x, float y)
    {
      return (x >= _leftTop.X) && (x <= _rightBottom.X) &&
             (y >= _leftTop.Y) && (y <= _rightBottom.Y);
    }

    /// <summary>
    /// Checks if the rectangle intersects with the given rectangle.
    /// </summary>
    /// <param name="rectangle">The rectangle to check intersection.</param>
    /// <returns>True if the rectangles intersect, else false.</returns>
    public bool Intersects(Rectangle rectangle)
    {
      return (((rectangle.LeftTop.X >= _leftTop.X) && (rectangle.LeftTop.X <= _rightBottom.X)) || ((rectangle.RightBottom.X >= _leftTop.X) && (rectangle.RightBottom.X <= _rightBottom.X))) &&
             (((rectangle.LeftTop.Y >= _leftTop.Y) && (rectangle.LeftTop.Y <= _rightBottom.Y)) || ((rectangle.RightBottom.Y >= _leftTop.Y) && (rectangle.RightBottom.Y <= _rightBottom.Y)));
    }

    /// <summary>
    /// Calculates the intersection rectangle of the two rectangles. This is the part where the two rectangles overlap.
    /// Be sure to check if the rectangles really intersect using the <code>Intersects</code> method before calling this method. 
    /// Otherwise an invalid rectangle with negative width and height is returned.
    /// </summary>
    /// <param name="rectangle">The other rectangle.</param>
    /// <returns>The intersection rectangle of the two rectangles.</returns>
    public Rectangle GetIntersection(Rectangle rectangle)
    {
      Rectangle result = new Rectangle();

      result.LeftTop.X = Misc.Max(rectangle.LeftTop.X, _leftTop.X);
      result.LeftTop.Y = Misc.Max(rectangle.LeftTop.Y, _leftTop.Y);

      result.RightBottom.X = Misc.Min(rectangle.RightBottom.X, _rightBottom.X);
      result.RightBottom.Y = Misc.Min(rectangle.RightBottom.Y, _rightBottom.Y);

      return result;
    }

  }
}
